// resumable experimental header

/***
*resumable
*
*       Copyright (c) Microsoft Corporation. All rights reserved.
*
*       Purpose: Library support of coroutines
*       http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0057r3.pdf
*
*       [Public]
*
****/
#pragma once
#ifndef _EXPERIMENTAL_RESUMABLE_
#define _EXPERIMENTAL_RESUMABLE_
#ifndef RC_INVOKED
#include <new>
#include <memory>
#if _HAS_EXCEPTIONS
#include <exception>
#endif
#include <type_traits>
#include <stdint.h>

#pragma pack(push, _CRT_PACKING)
#pragma push_macro("new")
#undef new

// intrinsics used in implementation of coroutine_handle
extern "C" size_t _coro_resume(void *);
extern "C" void _coro_destroy(void *);
extern "C" size_t _coro_done(void *);
#pragma intrinsic(_coro_resume)
#pragma intrinsic(_coro_destroy)
#pragma intrinsic(_coro_done)

_STD_BEGIN

namespace experimental {

	// TEMPLATE CLASS coroutine_traits
	template <typename _Ret, typename... _Ts>
	struct coroutine_traits
	{
		using promise_type = typename _Ret::promise_type;
	};

	// TEMPLATE CLASS coroutine_handle
	template <typename _PromiseT = void>
	struct coroutine_handle;

	// TEMPLATE CLASS coroutine_handle<void> - no promise access
	template <>
	struct coroutine_handle<void>
	{
		coroutine_handle() _NOEXCEPT = default;

		coroutine_handle(std::nullptr_t) _NOEXCEPT : _Ptr(nullptr)
		{
		}

		coroutine_handle &operator=(nullptr_t) _NOEXCEPT
		{
			_Ptr = nullptr;
			return *this;
		}

		static coroutine_handle from_address(void *_Addr) _NOEXCEPT
		{
			coroutine_handle _Result;
			_Result._Ptr = reinterpret_cast<_Resumable_frame_prefix *>(_Addr);
			return _Result;
		}

		void *address() const _NOEXCEPT
		{
			return _Ptr;
		}

		__declspec(deprecated("is deprecated. Use coroutine_handle::address() instead"))
		void *to_address() const _NOEXCEPT
		{
			return _Ptr;
		}

		void operator()() const _NOEXCEPT
		{
			resume();
		}

		explicit operator bool() const _NOEXCEPT
		{
			return _Ptr != nullptr;
		}

		void resume() const
		{
			_coro_resume(_Ptr);
		}

		void destroy()
		{
			_coro_destroy(_Ptr);
		}

		bool done() const
		{
			// REVISIT: should return _coro_done() == 0; when intrinsic is
			// hooked up
			return (_Ptr->_Index == 0);
		}

		struct _Resumable_frame_prefix
		{
			typedef void(__cdecl *_Resume_fn)(void *);
			_Resume_fn _Fn;
			uint16_t _Index;
			uint16_t _Flags;
		};

	protected:
		_Resumable_frame_prefix *_Ptr;
	};

	// TEMPLATE CLASS coroutine_handle<_PromiseT> - general form
	template <typename _PromiseT>
	struct coroutine_handle : coroutine_handle<>
	{
		coroutine_handle() _NOEXCEPT = default;

		using coroutine_handle<>::coroutine_handle;

		__declspec(deprecated("with pointer parameter is deprecated. Use coroutine_handle::from_promise(T&) instead"))
		static coroutine_handle from_promise(_PromiseT *_Prom) _NOEXCEPT
		{
			return from_promise(*_Prom);
		}

		static coroutine_handle from_promise(_PromiseT &_Prom) _NOEXCEPT
		{
			auto _FramePtr = reinterpret_cast<char *>(_STD addressof(_Prom)) + _ALIGNED_SIZE;
			coroutine_handle<_PromiseT> _Result;
			_Result._Ptr =
				reinterpret_cast<_Resumable_frame_prefix *>(_FramePtr);
			return _Result;
		}

		coroutine_handle &operator=(nullptr_t) _NOEXCEPT
		{
			_Ptr = nullptr;
			return *this;
		}

		static const size_t _ALIGN_REQ = sizeof(void *) * 2;

		static const size_t _ALIGNED_SIZE =
			is_empty<_PromiseT>::value
				? 0
				: ((sizeof(_PromiseT) + _ALIGN_REQ - 1) & ~(_ALIGN_REQ - 1));

		_PromiseT &promise() _NOEXCEPT
		{
			return *reinterpret_cast<_PromiseT *>(
				reinterpret_cast<char *>(_Ptr) - _ALIGNED_SIZE);
		}

		_PromiseT const &promise() const _NOEXCEPT
		{
			return *reinterpret_cast<_PromiseT const *>(
				reinterpret_cast<char const *>(_Ptr) - _ALIGNED_SIZE);
		}
	};

	template <typename _PromiseT>
	bool operator==(coroutine_handle<_PromiseT> const &_Left,
					coroutine_handle<_PromiseT> const &_Right) _NOEXCEPT
	{
		return _Left.address() == _Right.address();
	}

	template <typename _PromiseT>
	bool operator!=(coroutine_handle<_PromiseT> const &_Left,
					coroutine_handle<_PromiseT> const &_Right) _NOEXCEPT
	{
		return !(_Left == _Right);
	}

	// trivial awaitables

	struct suspend_if
	{
		bool _Ready;

		explicit suspend_if(bool _Condition) _NOEXCEPT : _Ready(!_Condition)
		{
		}

		bool await_ready() _NOEXCEPT
		{
			return _Ready;
		}

		void await_suspend(coroutine_handle<>) _NOEXCEPT
		{
		}

		void await_resume() _NOEXCEPT
		{
		}
	};

	struct suspend_always
	{
		bool await_ready() _NOEXCEPT
		{
			return false;
		}

		void await_suspend(coroutine_handle<>) _NOEXCEPT
		{
		}

		void await_resume() _NOEXCEPT
		{
		}
	};

	struct suspend_never
	{
		bool await_ready() _NOEXCEPT
		{
			return true;
		}

		void await_suspend(coroutine_handle<>) _NOEXCEPT
		{
		}

		void await_resume() _NOEXCEPT
		{
		}
	};

	// _Resumable_helper_traits class isolates front-end from public surface
	// naming changes

	template <typename _Ret, typename... _Ts>
	struct _Resumable_helper_traits
	{
		using _Traits = coroutine_traits<_Ret, _Ts...>;
		using _PromiseT = typename _Traits::promise_type;
		using _Handle_type = coroutine_handle<_PromiseT>;

		static _PromiseT *_Promise_from_frame(void *_Addr) _NOEXCEPT
		{
			return reinterpret_cast<_PromiseT *>(
				reinterpret_cast<char *>(_Addr) - _Handle_type::_ALIGNED_SIZE);
		}

		static _Handle_type _Handle_from_frame(void *_Addr) _NOEXCEPT
		{
			return _Handle_type::from_promise(*_Promise_from_frame(_Addr));
		}

		static void _Set_exception(void *_Addr)
		{
			_Promise_from_frame(_Addr)->set_exception(_STD current_exception());
		}

		static void _ConstructPromise(void *_Addr, void *_Resume_addr, int _HeapElision)
		{
			*reinterpret_cast<void **>(_Addr) = _Resume_addr;
			*reinterpret_cast<uint32_t *>(reinterpret_cast<uintptr_t>(_Addr) + sizeof(void *))
				= 2 + (_HeapElision ? 0 : 0x10000);
			auto _Prom = _Promise_from_frame(_Addr);
			::new (static_cast<void *>(_Prom)) _PromiseT();
		}

		static void _DestructPromise(void *_Addr)
		{
			_Promise_from_frame(_Addr)->~_PromiseT();
		}
	};
} // namespace experimental

_STD_END

// resumable functions support intrinsics

extern "C" size_t _coro_frame_size();
extern "C" void *_coro_frame_ptr();
extern "C" void _coro_init_block();
extern "C" void *_coro_resume_addr();
extern "C" void _coro_init_frame(void *);
extern "C" void _coro_save(size_t);
extern "C" void _coro_suspend(size_t);
extern "C" void _coro_cancel();
extern "C" void _coro_resume_block();

#pragma intrinsic(_coro_frame_size)
#pragma intrinsic(_coro_frame_ptr)
#pragma intrinsic(_coro_init_block)
#pragma intrinsic(_coro_resume_addr)
#pragma intrinsic(_coro_init_frame)
#pragma intrinsic(_coro_save)
#pragma intrinsic(_coro_suspend)
#pragma intrinsic(_coro_cancel)
#pragma intrinsic(_coro_resume_block)

#pragma pop_macro("new")
#pragma pack(pop)
#endif /* RC_INVOKED */
#endif /* _EXPERIMENTAL_RESUMABLE_ */
